package com.hunan.api.catv.service.biz.comment;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hunan.api.catv.common.CacheName;
import com.hunan.api.catv.common.ConstantsMsg;
import com.hunan.api.catv.entity.ServiceResult;
import com.hunan.api.catv.entity.po.Comment;
import com.hunan.api.catv.entity.po.Praise;
import com.hunan.api.catv.entity.vo.CommentVo;
import com.hunan.api.catv.entity.vo.MemberVo;
import com.hunan.api.catv.entity.vo.NewsVo;
import com.hunan.api.catv.service.ResultService;
import com.hunan.api.catv.service.biz.member.MemberBiz;
import com.hunan.api.catv.service.biz.news.NewsBiz;
import com.hunan.api.catv.service.biz.praise.PraiseBiz;
import com.hunan.api.catv.service.impl.comment.CommentService;
import com.hunan.api.catv.service.impl.tencent_cloud.tms.TmsService;
import com.hunan.api.catv.utils.PageUtils;
import com.hunan.api.catv.utils.RedisUtils;
import com.tencentcloudapi.tms.v20200713.models.TextModerationResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author jiang zhuan
 * @version 1.0
 * @description 评论
 * @date 2020/9/22 0022
 * @package com.hunan.api.catv.service.biz.comment
 * @company 湖南有线
 */
@Service
@CacheConfig(cacheNames = CacheName.comment)
public class CommentBiz extends ResultService {

    private static final String ENABLE_DEL_CACHE = CacheName.comment + "," + CacheName.ugcNews + "," + CacheName.pgcNews;
    private static final Integer APPROVAL_STATUS_CHECK = 0;
    private static final Integer APPROVAL_STATUS_PASS = 1;
    private static final Integer APPROVAL_STATUS_NO_PASS = 2;
    private static final Integer AUTO_REVIEW_REVIEW_RESULT = 0;
    private static final Integer AUTO_REVIEW_PASS_RESULT = 1;
    private static final Integer AUTO_REVIEW_BLOCK_RESULT = 2;
    private static final Integer COMMENT_LIKE_YES = 1;
    private static final Integer COMMENT_LIKE_NO = 0;
    private static final Integer FIRST_LEVEL = 1;
    private static final Integer SECOND_LEVEL = 2;


    @Autowired
    private CommentService commentService;

    @Autowired
    private PraiseBiz praiseBiz;

    @Autowired
    private CommentBiz commentBiz;

    @Autowired
    private MemberBiz memberBiz;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private TmsService tmsService;

    @Autowired
    NewsBiz newsBiz;

    @Value("${news.comment.counts}")
    private Integer newsCommentCounts;


    /**
     * 分页获取评论数
     */
    @Cacheable(unless = "#result == null")
    public PageInfo<CommentVo> page(int pageNum, int pageSize, String answerId, Integer answerType, Long firstLevelId, Integer memberId) {
        List<CommentVo> listVo = new LinkedList<>();
        List<Long> idsList = commentBiz.idsList(answerId, answerType, firstLevelId);
        for (Long id : idsList) {
            CommentVo commentVo = commentBiz.info(id);
            setMemberInfo(commentVo);
            if (!ObjectUtils.isEmpty(commentVo)) {
                getPraiseType(memberId, commentVo);
                listVo.add(commentVo);
            }
        }
        listVo = sortsComments(firstLevelId, listVo);
        return PageUtils.list2PageInfo(pageNum, pageSize, listVo);
    }

    private void setMemberInfo(CommentVo commentVo) {
        MemberVo memberVo = Optional.ofNullable(memberBiz.cacheInfo(commentVo.getMemberId())).orElse(new MemberVo());
        commentVo.setNickName(memberVo.getNickName());
        commentVo.setAvatarUrl(memberVo.getAvatarUrl());
    }

    private List<CommentVo> sortsComments(Long firstLevelId, List<CommentVo> listVo) {
        if (firstLevelId == null) {
            Map<Long, List<CommentVo>> firstLevel = listVo.stream().filter(commentVo -> commentVo.getFirstLevelId().equals(commentVo.getCommentId())).collect(Collectors.groupingBy(commentVo -> commentVo.getFirstLevelId()));
            Map<Long, List<CommentVo>> secondLevel = listVo.stream().filter(commentVo -> !commentVo.getFirstLevelId().equals(commentVo.getCommentId())).collect(Collectors.groupingBy(commentVo -> commentVo.getFirstLevelId()));
            firstLevel.forEach((aLong, commentVos) -> commentVos.forEach(commentVo -> commentVo.setSubList(secondLevel.get(aLong))));
            listVo.clear();
            for (Map.Entry<Long, List<CommentVo>> longListEntry : firstLevel.entrySet()) {
                listVo.addAll(longListEntry.getValue());
            }

            listVo = listVo.stream().sorted(Comparator.comparingInt(CommentVo::getPraiseCount).reversed()).collect(Collectors.toList());
            List<CommentVo> praiseList = listVo.stream().filter(commentVo -> commentVo.getPraiseCount() >= 10).collect(Collectors.toList());
            List<CommentVo> headList = null;
            List<CommentVo> tailList = null;
            if (praiseList.size() >= 5) {
                headList = listVo.subList(0, 5);
                tailList = listVo.subList(5, listVo.size()).stream().sorted(Comparator.comparing(CommentVo::getCreateTime).reversed()).collect(Collectors.toList());
            } else {
                headList = new ArrayList<>();
                tailList = listVo.stream().sorted(Comparator.comparing(CommentVo::getCreateTime).reversed()).collect(Collectors.toList());
            }
            listVo = Stream.of(headList, tailList).flatMap(Collection::stream).collect(Collectors.toList());

        } else {
            listVo = listVo.stream().sorted(Comparator.comparing(CommentVo::getCreateTime).reversed()).collect(Collectors.toList());
        }
        return listVo;
    }

    /**
     * 分页获取评论数
     */
    @Cacheable(unless = "#result == null")
    public PageInfo<CommentVo> pageWithoutMemberId(int pageNum, int pageSize, String answerId, Integer answerType, Long firstLevelId) {
        List<CommentVo> listVo = new LinkedList<>();
        List<Long> idsList = commentBiz.idsList(answerId, answerType, firstLevelId);
        for (Long id : idsList) {
            CommentVo commentVo = commentBiz.info(id);
            setMemberInfo(commentVo);
            if (!ObjectUtils.isEmpty(commentVo)) {
                //getPraiseType(memberId, commentVo);
                listVo.add(commentVo);
            }
        }
        listVo = sortsComments(firstLevelId, listVo);
        return PageUtils.list2PageInfo(pageNum, pageSize, listVo);
    }

    private void getPraiseType(Integer memberId, CommentVo commentVo) {
        Praise praise = getPraise(memberId, commentVo.getCommentId());
        // 是否作者点赞 0或1 是否本人点赞 0或2 重合则相加
        if (commentVo.getAuthorPraise().equals(1)) {
            if (praise == null) {
                commentVo.setPraiseState(1);
            } else {
                commentVo.setPraiseState(3);
            }
        } else {
            if (praise == null) {
                commentVo.setPraiseState(0);
            } else {
                commentVo.setPraiseState(2);
            }
        }
        NewsVo newsvo = Optional.ofNullable(newsBiz.getNewsInfo(Integer.valueOf(commentVo.getAnswerId()))).orElse(new NewsVo());
        if (commentVo.getMemberId().equals(newsvo.getMemberId())) {
            commentVo.setOwner(1);
        } else {
            commentVo.setOwner(0);
        }
    }

    @Cacheable(unless = "#result == null")
    public Praise getPraise(Integer memberId, Long commentId) {
        return new Praise().selectOne(new QueryWrapper<Praise>().eq(Praise.BE_PRAISE_ID, commentId).eq(Praise.MEMBER_ID, memberId).eq(Praise.STATE, COMMENT_LIKE_YES));
    }

    /**
     * 获取视频且状态为通过的评论Id
     *
     * @param answerId
     * @param answerType
     * @return
     */
//    @Cacheable(unless = "#result == null")
    public List<Long> idsList(String answerId, Integer answerType, Long firstLevelId) {
        Comment comment = new Comment();
        comment.setState(APPROVAL_STATUS_PASS);
        comment.setAnswerId(answerId);
        comment.setAnswerType(answerType);
        comment.setFirstLevelId(firstLevelId);
        return commentService.listIds(comment);
    }

    /**
     * 获取视频且状态为待审核和通过的评论Id
     *
     * @param answerId
     * @param answerType
     * @return
     */
    @Cacheable(unless = "#result == null")
    public List<Long> idsListExcludeFail(String answerId, Integer answerType, Long firstLevelId) {
        Comment comment = new Comment();
        comment.setState(APPROVAL_STATUS_NO_PASS);
        comment.setAnswerId(answerId);
        comment.setAnswerType(answerType);
        comment.setFirstLevelId(firstLevelId);
        return commentService.listIdsExcludeFail(comment);
    }

    /**
     * 获取详细评论VO
     *
     * @param commentId
     * @return
     */
    @Cacheable(unless = "#result == null")
    public CommentVo info(Long commentId) {
        CommentVo commentVo = Optional.ofNullable(commentService.getById(commentId)).orElse(new CommentVo());
        Long parentId = commentVo.getParentId();
        if (parentId != null) {
            CommentVo parentComent = Optional.ofNullable(commentService.getById(parentId)).orElse(new CommentVo());
            MemberVo info = Optional.ofNullable(memberBiz.info(parentComent.getMemberId())).orElse(new MemberVo());
            commentVo.setBeAnswerMemberName(info.getNickName());
        }
        return commentVo;
    }

    /**
     * 添加评论
     *
     * @param comment
     * @param memberId
     * @return
     */
    @Transactional
    public ServiceResult add(Comment comment, Integer memberId) {
        Boolean isNotRequest = redisUtils.setNX("CommentSafe"+comment, "1", 3, TimeUnit.SECONDS);
        if (!isNotRequest) {
            return buildFail("");
        }
        if (memberId == null) {
            return buildFail(ConstantsMsg.getUserInfo_fail);
        }
        comment.setMemberId(memberId);
        TextModerationResponse response = tmsService.textModeration(comment.getContent().replaceAll("\\s", ""));
        String requestId = response.getRequestId();
        String suggestion = response.getSuggestion();
        comment.setAutoReviewId(requestId);
        Integer autoReviewResult = "Pass".equals(suggestion) ? AUTO_REVIEW_PASS_RESULT : "Review".equals(suggestion) ? AUTO_REVIEW_REVIEW_RESULT : AUTO_REVIEW_BLOCK_RESULT;
        comment.setAutoReviewResult(autoReviewResult);
        comment.setState("Pass".equals(suggestion) ? APPROVAL_STATUS_PASS : APPROVAL_STATUS_CHECK);
        comment.setReviewTime("Pass".equals(suggestion) ? new Date() : null);
        comment.setPraiseCount(0);
        comment.setAnswerCount(0);
        Long parentId = comment.getParentId();
        comment.setLevel(parentId == null ? FIRST_LEVEL : SECOND_LEVEL);
        comment.setCreateTime(new Date());
        comment.setUpdateTime(new Date());
        boolean insert = comment.insert();
        if (parentId != null) {
            addParentAnswerCount(parentId);
            comment.setFirstLevelId(new Comment().selectOne(new QueryWrapper<Comment>().eq(Comment.COMMENT_ID, parentId)).getFirstLevelId());
            if (!parentId.equals(comment.getFirstLevelId())) {
                //一级评论增加回复数量
                addParentAnswerCount(comment.getFirstLevelId());
            }
        } else {
            comment.setFirstLevelId(comment.getCommentId());
        }
        comment.updateById();

        if (insert) {
//            delCommentCountState();
            delCommentCache(comment.getAnswerId(), comment.getAnswerType());
            CommentVo commentVo = CommentVo.create();
            BeanUtil.copyProperties(comment, commentVo);
            if (parentId != null) {
                CommentVo parentComent = Optional.ofNullable(commentService.getById(parentId)).orElse(new CommentVo());
                MemberVo info = Optional.ofNullable(memberBiz.info(parentComent.getMemberId())).orElse(new MemberVo());
                commentVo.setBeAnswerMemberName(info.getNickName());
            }
            NewsVo newsvo = Optional.ofNullable(newsBiz.getById(Integer.valueOf(commentVo.getAnswerId()))).orElse(new NewsVo());
            if (newsvo.getMemberId().equals(memberId)) {
                commentVo.setOwner(1);
            }
            if (!ObjectUtils.isEmpty(commentVo)) {
                Integer exsitMemberId = commentVo.getMemberId();
                MemberVo memberVo = Optional.ofNullable(memberBiz.info(exsitMemberId)).orElse(new MemberVo());
                commentVo.setNickName(memberVo.getNickName());
                commentVo.setAvatarUrl(memberVo.getAvatarUrl());
            }
            commentVo.setCreateTime("刚刚");
            return buildOK(commentVo);
        }
        return buildFail(ConstantsMsg.admin_add_fail);
    }

    private void delCommentCache(String answerId, Integer answerType) {
        redisUtils.delObjectCacheListPattern(CacheName.comment + "::page" + "*" + answerId + "." + answerType + "*");
    }

    @Transactional
    public void addParentAnswerCount(Long parentId) {
        Comment comment = new Comment().selectOne(new QueryWrapper<Comment>().eq(Comment.COMMENT_ID, parentId));
        Integer before = comment.getAnswerCount();
        comment.setAnswerCount(++before);
        comment.updateById();
        delComentInfoCache(comment);
    }

    /**
     * 添加评论删除缓存
     */
    private void delCommentCountState() {
        String[] cacheNames = ENABLE_DEL_CACHE.split(",");
        redisUtils.delObjectCacheList(cacheNames);

    }

    /**
     * 获取评论数通过的
     *
     * @param answerId
     * @param answerType
     * @return
     */
    public Map<String, Integer> count(String answerId, Integer answerType) {
        Map<String, Integer> result = new HashMap<>();
        Comment comment = new Comment();
        comment.setState(APPROVAL_STATUS_PASS);
        comment.setAnswerId(answerId);
        comment.setAnswerType(answerType);
        result.put("amount", commentService.count(comment));
        return result;
    }

    /**
     * 获取评论数排除未通过，通过和未审核
     *
     * @param answerId
     * @param answerType
     * @return
     */
    public Map<String, Integer> countExcludeFail(String answerId, Integer answerType) {
        Map<String, Integer> result = new HashMap<>();
        Comment comment = new Comment();
        comment.setState(APPROVAL_STATUS_NO_PASS);
        comment.setAnswerId(answerId);
        comment.setAnswerType(answerType);
        result.put("amount", commentService.countExcludeFail(comment));
        return result;
    }

    /**
     * 更新点赞数
     *
     * @param bePraiseId
     * @param memberId
     * @param type
     * @return
     */
    @Transactional
    public ServiceResult updatePraiseCount(String bePraiseId, Integer memberId, Integer beMemberId, Integer type, Integer bePraiseType) {
        if (memberId == null) {
            return buildFail(ConstantsMsg.member_simpleInfo_fail);
        }
        CommentVo commentVo = commentService.getById(Long.valueOf(bePraiseId));
        if (commentVo == null) {
            return buildFail(ConstantsMsg.RESOURCE_DOES_NOT_EXIST_INOPERABLE);
        }
        Integer praiseCount = commentVo.getPraiseCount();
        String answerId = commentVo.getAnswerId();
        NewsVo news = Optional.ofNullable(newsBiz.getById(Integer.valueOf(answerId))).orElse(new NewsVo());
        Integer author = news.getMemberId();
        if (memberId.equals(author)) {
            if (type.equals(1)) {
                commentVo.setAuthorPraise(1);
            } else {
                commentVo.setAuthorPraise(0);
            }
        }
        Praise praise = new Praise().selectOne(new QueryWrapper<Praise>().
                eq(bePraiseId != null, Praise.BE_PRAISE_ID, String.valueOf(bePraiseId)).
                eq(Praise.BE_PRAISE_TYPE, bePraiseType).
                eq(Praise.MEMBER_ID, memberId).
                eq(Praise.BE_MEMBER_ID, beMemberId));
        if (praise == null) {
            if (type.equals(COMMENT_LIKE_YES)) {
                praise = new Praise();
                praise.setBePraiseId(String.valueOf(bePraiseId));
                praise.setBePraiseType(bePraiseType);
                praise.setMemberId(memberId);
                praise.setBeMemberId(beMemberId);
                commentVo.setPraiseCount(praiseCount + 1);
                praise.setCreateTime(new Date());
                praise.setUpdateTime(new Date());
                praise.setState(type);
                praise.insert();
                updatePraiseCache(praise, type);
            } else {
                return buildFail(ConstantsMsg.COMMENT_LIKE_UPDATE_EXCPITON);
            }
        } else {
            if (type.equals(COMMENT_LIKE_YES) && praise.getState().equals(COMMENT_LIKE_NO)) {
                commentVo.setPraiseCount(praiseCount + 1);
                praise.setState(type);
                praise.setUpdateTime(new Date());
                praise.updateById();
                updatePraiseCache(praise, type);
            } else if (type.equals(COMMENT_LIKE_NO) && praise.getState().equals(COMMENT_LIKE_YES)) {
                commentVo.setPraiseCount(praiseCount - 1);
                praise.setState(type);
                praise.setUpdateTime(new Date());
                praise.updateById();
                updatePraiseCache(praise, type);
            } else {
                return buildFail(ConstantsMsg.COMMENT_LIKE_UPDATE_EXCPITON);
            }
        }
        Comment comment = new Comment();
        BeanUtils.copyProperties(commentVo, comment);
        ServiceResult result = commentService.update(comment);
        if (result.getStatus()) {
            delComentInfoCache(comment);
            delComentPageCache(comment);
            return result;
        }

        return buildFail(ConstantsMsg.COMMENT_LIKE_UPDATE_FAIL);
    }

    private void updatePraiseCache(Praise praise, Integer type) {
        if (type.equals(COMMENT_LIKE_YES)) {
            redisUtils.set(CacheName.comment + "::getPraise." + praise.getMemberId() + "." + praise.getBePraiseId(), praise);
        } else {
            redisUtils.del(CacheName.comment + "::getPraise." + praise.getMemberId() + "." + praise.getBePraiseId());
        }
    }

    private void delComentInfoCache(Comment comment) {
        redisUtils.delObjectCacheListPattern(CacheName.comment + "::info" + "*." + comment.getCommentId());
    }

    private void delComentPageCache(Comment comment) {
        redisUtils.delObjectCacheListPattern(CacheName.comment + "::page" + "*" + comment.getAnswerId() + "." + comment.getAnswerType() + "*");
    }

    @Deprecated
    public PageInfo<CommentVo> getCommentList(Integer pageNum, Integer pageSize, String memberPhone, Integer answerId, String memberName, Integer state, String startTime, String endTime, String reviewStartTime, String reviewEndTime) {
        PageHelper.startPage(pageNum, pageSize);
        List<CommentVo> commentList = commentService.getCommentList(memberPhone, answerId, memberName, state, startTime, endTime, reviewStartTime, reviewEndTime);
        return new PageInfo<>(commentList);
    }

    @Deprecated
    @Transactional(rollbackFor = Exception.class)
    public ServiceResult batchUpdateState(String commentIds, Integer type) {
        List<String> commentIdList = Stream.of(commentIds.split(",")).collect(Collectors.toList());
        for (String commentId : commentIdList) {
            Comment comment = new Comment().selectById(commentId);
            if (0 == type) {
                comment.setState(1);
            } else {
                comment.setState(2);
            }
            comment.setReviewTime(new Date());
            boolean update = comment.updateById();
            if (!update) {
                return buildFail(ConstantsMsg.admin_edit_fail, commentId);
            }
        }
        delCommentCountState();
        return buildOK(ConstantsMsg.admin_edit_success);
    }

    /**
     * 获取点赞数
     *
     * @param commentId
     * @return
     */
    public Map<String, Integer> countPraise(Long commentId) {
        CommentVo commentVo = commentService.getById(commentId);
        HashMap<String, Integer> result = new HashMap<>();
        result.put("likeAmount", commentVo.getPraiseCount());
        return result;
    }

    /**
     * 获取该短视频下热评
     */
    public CommentVo getHotCommentsByNewsId(Integer newsId) {
        //获取评论列表
        //展示规则：
        //评论赞数  ≥ 10条，展示赞数最多的一条评论。如赞数一样，则展示时间最早的一条
        //评论赞数 ＜ 10条，隐藏热评
        List<Comment> commentList = new Comment().selectList(new QueryWrapper<Comment>().eq(Comment.ANSWER_ID, newsId)
                .eq(Comment.ANSWER_TYPE, 0)
                .eq(Comment.LEVEL, 1)
                .eq(Comment.STATE, 1)
                .eq(Comment.DELETED, 1)
                .orderByDesc(Comment.PRAISE_COUNT).orderByAsc(Comment.CREATE_TIME));
        CommentVo commentVo = new CommentVo();
        if (CollectionUtils.isEmpty(commentList)) {
            return commentVo;
        }
        String commentCounts = String.valueOf(commentList.size());
        Comment comment = commentList.get(0);
        if (comment.getPraiseCount() >= newsCommentCounts) {
            BeanUtils.copyProperties(comment, commentVo);
            MemberVo info = memberBiz.info(comment.getMemberId());
            if (info != null) {
                commentVo.setNickName(info.getNickName());
                commentVo.setAvatarUrl(info.getAvatarUrl());
            }
        }
        commentVo.setCommentCounts(commentCounts);
        return commentVo;
    }

    public PageInfo<CommentVo> wrapPage(Integer pageNum, Integer pageSize, String answerId, Integer answerType, Long firstLevelId, Integer memberIdV2) {
        PageInfo<CommentVo> page = new PageInfo<>();
        if (memberIdV2 == null) {
            page = commentBiz.pageWithoutMemberId(pageNum, pageSize, answerId, answerType, firstLevelId);
        } else {
            page = commentBiz.page(pageNum, pageSize, answerId, answerType, firstLevelId, memberIdV2);
        }
        List<CommentVo> list = page.getList();
        list.stream().map(commentVo -> {
            if (commentVo.getSubList() != null && !commentVo.getSubList().isEmpty()) {
                List<CommentVo> subList = commentVo.getSubList();
                subList.stream().map(subCommentVo -> getWrapCommentVo(subCommentVo)).collect(Collectors.toList());
            }
            return getWrapCommentVo(commentVo);
        }).collect(Collectors.toList());
        return page;
    }

    private CommentVo getWrapCommentVo(CommentVo commentVo) {
        String createTime = commentVo.getCreateTime();
        LocalDateTime ctime = LocalDateTime.parse(createTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime now = LocalDateTime.now();

        if (ctime.plusMinutes(1L).compareTo(now) > 0) {
            commentVo.setCreateTime("1分钟前");
            return commentVo;
        }
        if (ctime.plusHours(1L).compareTo(now) > 0) {
            Duration between = Duration.between(ctime, now);
            commentVo.setCreateTime(between.toMinutes() + "分钟前");
            return commentVo;
        }
        if (ctime.plusHours(24L).compareTo(now) > 0) {
            Duration between = Duration.between(ctime, now);
            commentVo.setCreateTime(between.toHours() + "小时前");
            return commentVo;
        }
        if (ctime.plusHours(48L).compareTo(now) > 0) {
            commentVo.setCreateTime("昨天");
            return commentVo;
        }
        if (ctime.plusDays(4L).compareTo(now) > 0) {
            Duration between = Duration.between(ctime, now);
            commentVo.setCreateTime(between.toDays() + "天前");
            return commentVo;
        }
        commentVo.setCreateTime(ctime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        return commentVo;
    }
}
